package com.daifeng.easysyncserver.broadcast;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

/**
 * @author daifeng
 */
@Service
@Slf4j
public class BroadcastClient {

    private volatile DatagramSocket socket = null;

    @Value("${socket.broadcast-port}")
    private Integer broadcastPort;

    public void broadcast(String broadcastMessage, InetAddress address) throws IOException {
        DatagramSocket socket = getDatagramSocketInstance();

        byte[] buffer = broadcastMessage.getBytes();

        DatagramPacket packet
                = new DatagramPacket(buffer, buffer.length, address, broadcastPort);
        log.info("广播消息：{}", broadcastMessage);
        socket.send(packet);
    }

    /**
     * 获取广播socket
     * @return
     * @throws SocketException
     */
    public DatagramSocket getDatagramSocketInstance() throws SocketException {
        if (socket == null) {
            synchronized (this) {
                if (socket == null) {
                    socket = new DatagramSocket();
                    socket.setBroadcast(true);
                }
            }
        }
        return socket;
    }

    /**
     * 获取所有的广播地址
     * @return
     * @throws SocketException
     */
    public List<InetAddress> listAllBroadcastAddresses() throws SocketException {
        List<InetAddress> broadcastList = new ArrayList<>();
        Enumeration<NetworkInterface> interfaces
                = NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface networkInterface = interfaces.nextElement();

            if (networkInterface.isLoopback() || !networkInterface.isUp()) {
                continue;
            }

            networkInterface.getInterfaceAddresses().stream()
                    .map(a -> a.getBroadcast())
                    .filter(Objects::nonNull)
                    .forEach(broadcastList::add);
        }
        log.info("广播地址：{}", broadcastList);
        return broadcastList;
    }

    /**
     * 启动服务端监听
     */
    @PostConstruct
    private void receiver() {
        byte[] buffer = new byte[65507];
        Thread rt = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                DatagramSocket ds = new DatagramSocket(broadcastPort);
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                log.info("服务端启动监听{}", broadcastPort);
                while (true) {
                    ds.receive(packet);
                    String received = new String(packet.getData(), 0, packet.getLength());
                    log.info("收到消息：{}, 地址：{}", received, packet.getSocketAddress());
                    // 客户端请求连接
                    if(SocketConstants.CLIENT_LINK_REQUEST.equals(received)) {
                        notifyClient();
                    }
                }
            }
        });
        rt.start();
    }

    /**
     * 通知客户端服务器地址
     */
    @SneakyThrows
    void notifyClient() {
        listAllBroadcastAddresses().forEach(i -> {
            try {
                broadcast("Hello", i);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}
